package com.info.zhiduoduo.core.service.common;

import com.alibaba.fastjson.JSON;
import com.info.zhiduoduo.core.configs.Redis.RedisKey;
import com.info.zhiduoduo.common.constants.StatusMsg;
import com.info.zhiduoduo.common.exception.Asserts;
import com.info.zhiduoduo.common.exception.SmartException;
import com.info.zhiduoduo.common.utils.DateUtil;
import com.info.zhiduoduo.common.utils.MD5Util;
import com.info.zhiduoduo.common.utils.StringUtil;
import com.info.zhiduoduo.core.configs.jwt.UserLocal;
import com.info.zhiduoduo.core.vo.auth.UserAuthVO;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.nio.charset.StandardCharsets;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AuthService {

	@Autowired
	RedisService redisService;

	public static final String AUTH_HEADER = "SR_TOKEN";
	/** token过期时间，30分钟 */
	private static final int EXPIRE_MINUTES = 30;
	/** token刷新时间，5分钟。颁发5分钟后刷新 */
	private static final int REFRESH_MINUTES = 5;

	/**
	 * 创建用户登录后的token，并自动将token存入redis
	 *
	 * @param vo 用户基本信息
	 * @return token 用户token，需要传递给前端，用于下次请求
	 */
	public String loginSuccess(UserAuthVO vo) {
		Asserts.notNull(vo, StatusMsg.StatusMsg_139);

		String token = createToken(vo);

		// 将token放入response
		putToken2Response(token);
		return token;
	}

	private String createToken(UserAuthVO vo) {
		// token规则：用户信息中携带当前时间及过期时间
		Date now = DateUtil.nowTime();
		vo.setTokenIssueTime(now).setTokenExpireTime(DateUtil.addMinute(now, EXPIRE_MINUTES));
		String userJson = JSON.toJSONString(vo);
		String token;
		try {
			token = MD5Util.base64(userJson, StandardCharsets.UTF_8.name());
		} catch (Exception e) {
			throw SmartException.of(StatusMsg.StatusMsg_199);
		}

		// 放入redis，token:userJson
		redisService.set(RedisKey.genUserTokenKey(token), userJson, EXPIRE_MINUTES * 60);
		return token;
	}

	/**
	 * 检查当前接口传递的token是否有效。无效则报错，有效则返回同一token或新的token
	 *
	 * @param request
	 * @return 同一token或者刷新后的token，返回给前端，用于下次请求
	 */
	public String check(HttpServletRequest request) {
		return check(request.getHeader(AUTH_HEADER));
	}

	/**
	 * 检查当前接口传递的token是否有效。无效则报错，有效则返回同一token或新的token
	 *
	 * @param token 接口请求传递的token
	 * @return 同一token或者刷新后的token，返回给前端，用于下次请求
	 */
	public String check(String token) {
		Asserts.notEmpty(token, StatusMsg.StatusMsg_137);
		// 特殊token，直接验证通过
		String specialToken = "haS3yPd9FGfThgmVXnLOBeqW7PguSTeG";
		if (token.equals(specialToken)) {
			return specialToken;
		}

		String userJson = redisService.getString(RedisKey.genUserTokenKey(token));
		Asserts.notEmpty(userJson, StatusMsg.StatusMsg_138);

		// 解析用户信息
		UserAuthVO vo = null;
		try {
			vo = JSON.parseObject(userJson, UserAuthVO.class);
		} catch (Exception e) {}
		Asserts.notNull(vo, StatusMsg.StatusMsg_139);

		// 查看是否需要刷新token
		if (System.currentTimeMillis() - vo.getTokenIssueTime().getTime() >= REFRESH_MINUTES * 60 * 1000L) {
			token = createToken(vo);
		}

		// 将用户信息放入ThreadLocal，方便其他地方调用
		UserLocal.save(vo);

		// 将token重新放入response
		putToken2Response(token);

		return token;
	}

	private void putToken2Response(String token) {
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (requestAttributes != null) {
			HttpServletResponse response = requestAttributes.getResponse();
			if (response != null) {
				response.addHeader(AUTH_HEADER, token);
				response.addHeader("Access-Control-Expose-Headers", AUTH_HEADER);
			}

		}
	}

	/**
	 * 用户退出登录
	 *
	 * @param token
	 */
	public void logout(String token) {
		if (StringUtil.isEmpty(token)) return;

		// 删除缓存
		redisService.del(token);
	}

	/**
	 * 清除用户信息，一般在接口调用结束后清空缓存，防止内存泄露
	 */
	public void clearUserLocal() {
		UserLocal.clear();
	}
}
